﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using A2DFramework.ValidationService;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Reflection;
using A2DFramework.BusinessExceptionService.Exceptions;
using A2DFramework.BusinessExceptionService.Configuration;
using System.Text.RegularExpressions;
using A2DFramework.BusinessExceptionService.Helpers;
using A2DFramework.BusinessExceptionService.JavascriptGenerator;

namespace A2DFramework.BusinessExceptionService
{
    public class BusinessExceptionValidatorExecutor : IValidatorExecutor
    {
        private string scene = "";
        private RuleEngine.RuleEngine engine;
        private ErrorMessageHelper helper;
        private List<BrokenRule> errors = new List<BrokenRule>();

        public BusinessExceptionValidatorExecutor()
        {
            helper = new ErrorMessageHelper(this.errors);
            engine = new RuleEngine.RuleEngine();
            engine.SetParameter("helper", this.helper);
        }

        public List<BrokenRule> IsValid<T>(T o, string scene = "", bool only1Level = false)
        {
            this.scene = scene;
            return IsValid(typeof(T), o, only1Level);
        }

        private List<BrokenRule> IsValid(Type t, object o, bool only1Level)
        {
            var descriptor = GetTypeDescriptor(t);

            foreach (PropertyDescriptor propertyDescriptor in descriptor.GetProperties())
            {
                //检查是否在配置的规则中
                Rule rule=FindRule4Property(scene, t, propertyDescriptor.Name);

                //存在则检查
                if(rule!=null)
                    ValidateProperty(o, propertyDescriptor, rule);
            }

            //检查是否在配置的规则中
            Rule rule4Class = FindRule4Class(scene, t);

            //存在则检查
            if (rule4Class != null)
                ValidateClass(o, rule4Class);

            if (!only1Level)
            {
                if (o.GetType().IsClass && !o.GetType().Equals(typeof(string)))
                {
                    foreach (var p in o.GetType().GetProperties())
                    {
                        ParameterInfo[] pInfos = p.GetIndexParameters();
                        if (pInfos != null && pInfos.Count() > 0)
                        {
                            int rowIndex = 0;
                            foreach (var pInfo in pInfos)
                            {
                                object pValue = null;
                                while (true)
                                {
                                    try
                                    {
                                        pValue = p.GetValue(o, new object[] { rowIndex++ });
                                    }
                                    catch (Exception ex)
                                    {
                                        break;
                                    }
                                    if (pValue != null)
                                    {
                                        IsValid(p.PropertyType, pValue, only1Level);
                                    }
                                }
                            }
                        }
                        else
                        {
                            object pValue = p.GetValue(o, null);
                            if (pValue != null)
                            {
                                if (!p.PropertyType.IsValueType || p.PropertyType.IsEnum)
                                {
                                    IsValid(p.PropertyType, pValue, only1Level);
                                }
                            }
                        }
                    }
                }
            }

            return this.errors;
        }

        private void ValidateClass(object o, Rule rule)
        {
            BusinessExceptionServiceJavascriptGenerator generator = new BusinessExceptionServiceJavascriptGenerator(rule.RuleContent, rule.PropertyName, rule.ErrorMessage);
            engine.SetParameter("value", o);
            engine.SetJavascriptGenerator(generator);
            engine.Process();
            PostEngineProcess(rule);
        }

        private void ValidateProperty(object o, PropertyDescriptor propertyDescriptor, Rule rule)
        {
            BusinessExceptionServiceJavascriptGenerator generator = new BusinessExceptionServiceJavascriptGenerator(rule.RuleContent, rule.PropertyName, rule.ErrorMessage);
            engine.SetParameter("value", propertyDescriptor.GetValue(o));
            engine.SetJavascriptGenerator(generator);
            engine.Process();
            PostEngineProcess(rule);
        }
        private void PostEngineProcess(Rule rule)
        {
            bool result = engine.GetBoolean("result");
            if (!result)
            {
                if (rule.FailAction == FailAction.Warning)
                {
                    helper.AddErrorMessage(rule.PropertyName, rule.ErrorMessage);
                }
                else if (rule.FailAction == FailAction.Stop)
                {
                    throw new Exception("规则验证失败:" + rule.PropertyName);
                }
            }
        }

        private Rule FindRule4Property(string scene, Type type, string propertyName)
        {
            Scene sceneObj=null;
            if (!BESConfig.SceneMaps.ContainsKey(scene))
                return null;

            sceneObj = BESConfig.SceneMaps[scene];

            List<EntityType> entityTypes=sceneObj.EntityTypes.Where(t=>t.TargetType.Equals(type)).ToList();
            if (entityTypes == null || entityTypes.Count == 0)
                return null;


            foreach (var entityType in entityTypes)
            {
                foreach (var rule in entityType.Rules)
                {
                    if (rule.RuleType == RuleType.PropertyValidator)
                    {
                        if (propertyName == rule.PropertyName)
                            return rule;

                        if(Regex.IsMatch(propertyName, rule.PropertyName))
                            return rule;
                    }
                }
            }
            return null;
        }
        private Rule FindRule4Class(string scene, Type type)
        {
            Scene sceneObj = null;
            if (!BESConfig.SceneMaps.ContainsKey(scene))
                return null;

            sceneObj = BESConfig.SceneMaps[scene];

            List<EntityType> entityTypes = sceneObj.EntityTypes.Where(t => t.TargetType.Equals(type)).ToList();
            if (entityTypes == null || entityTypes.Count == 0)
                return null;


            foreach (var entityType in entityTypes)
                foreach (var rule in entityType.Rules)
                    if (rule.RuleType == RuleType.ClassValidator)
                        return rule;
            return null;
        }

        private ICustomTypeDescriptor GetTypeDescriptor(Type type)
        {
            return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
        }
    }
}
